package com.base.social.auth.request;

import cn.hutool.http.HttpUtil;
import com.base.core.lang.StringUtils;
import com.base.core.utils.JacksonUtils;
import com.base.core.utils.UrlBuilderUtils;
import com.base.core.utils.R;
import com.base.social.cache.AuthStateCache;
import com.base.social.config.AuthConfig;
import com.base.social.config.AuthDefaultSource;
import com.base.social.config.SignatureClassName;
import com.base.social.domain.AuthCallbackParams;
import com.base.social.domain.AuthToken;
import com.base.social.domain.AuthUser;
import com.base.social.enums.AuthUserGender;
import com.base.social.exception.AuthRuntimeException;
import com.base.social.auth.AuthDefault;
import com.base.social.signature.SignatureFactory;

import java.util.Map;

/**
 * qq登录
 *
 * @author becker
 */
public class AuthQQ extends AuthDefault {
    public AuthQQ(AuthConfig config) {
        super(config, AuthDefaultSource.QQ);
    }

    public AuthQQ(AuthConfig config, AuthStateCache authStateCache) {
        super(config, AuthDefaultSource.QQ, authStateCache);
    }

    @Override
    protected AuthToken createAccessToken(AuthCallbackParams authCallback) {
        String response = doGetAuthorizationCode(authCallback.getCode());
        return findAuthToken(response);
    }

    @Override
    public R refresh(AuthToken authToken) {
        String response = HttpUtil.get(refreshTokenUrl(authToken.getRefreshToken()));
        return R.success(findAuthToken(response));
    }

    @Override
    public AuthUser findUserInfo(AuthToken authToken) {
        String openId = this.getOpenId(authToken);
        String response = doGetUserInfo(authToken);
        Map<String, Object> object = JacksonUtils.toMap(response);
        if (Integer.parseInt(object.get("ret").toString()) != 0) {
            throw new AuthRuntimeException(object.get("msg").toString());
        }
        String avatar = object.get("figureurl_qq_2").toString();
        if (StringUtils.isEmpty(avatar)) {
            avatar = object.get("figureurl_qq_1").toString();
        }

        String location = String.format("%s-%s", object.get("province").toString(), object.get("city").toString());
        return AuthUser.builder()
                .username(object.get("nickname").toString())
                .nickname(object.get("nickname").toString())
                .avatar(avatar)
                .location(location)
                .openid(openId)
                .gender(AuthUserGender.realGender(object.get("gender").toString()))
                .token(authToken)
                .source(source.toString())
                .build();
    }

    /**
     * 获取QQ用户的OpenId，支持自定义是否启用查询unionid的功能，如果启用查询unionid的功能，
     * 那就需要开发者先通过邮件申请unionid功能，参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
     *
     * @param authToken 通过{@link AuthQQ#createAccessToken(AuthCallbackParams)}获取到的{@code authToken}
     * @return openId
     */
    private String getOpenId(AuthToken authToken) {
        String response = HttpUtil.get(UrlBuilderUtils.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
                .queryParam("access_token", authToken.getAccessToken())
                .queryParam("unionid", config.isUnionId() ? 1 : 0)
                .build());
        String removePrefix = response.replace("callback(", "");
        String removeSuffix = removePrefix.replace(");", "");
        String openId = removeSuffix.trim();
        Map<String, Object> object = JacksonUtils.toMap(openId);
        if (object.containsKey("error")) {
            throw new AuthRuntimeException(object.get("error") + ":" + object.get("error_description"));
        }
        authToken.setOpenId(object.get("openid").toString());
        if (object.containsKey("unionid")) {
            authToken.setUnionId(object.get("unionid").toString());
        }
        return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
    }

    /**
     * 返回获取userInfo的url
     *
     * @param authToken 用户授权token
     * @return 返回获取userInfo的url
     */
    @Override
    protected String userInfoUrl(AuthToken authToken) {
        return UrlBuilderUtils.fromBaseUrl(source.userInfo())
                .queryParam("access_token", authToken.getAccessToken())
                .queryParam("oauth_consumer_key", config.getClientId())
                .queryParam("openid", authToken.getOpenId())
                .build();
    }

    private AuthToken findAuthToken(String response) {
        Map<String, String> accessTokenObject = SignatureFactory.instance(SignatureClassName.DEFAULT.fetchClassName())
                .parseStringToMap(response);
        if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) {
            throw new AuthRuntimeException(accessTokenObject.get("msg"));
        }
        return AuthToken.builder()
                .accessToken(accessTokenObject.get("access_token"))
                .expireIn(Integer.parseInt(accessTokenObject.get("expires_in")))
                .refreshToken(accessTokenObject.get("refresh_token"))
                .build();
    }
}
